package com.allenkerr.third;

import com.alibaba.fastjson.JSON;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;

import javax.imageio.ImageIO;

/**
 * http://download.csdn.net/detail/u014112584/7518319
 * 图像相似度检测之直方图相交（基于颜色的图像检索）
 * 图像相似度检测之直方图相交（基于颜色的图像检索），传统直方图相交法，巴士系数法：欧式距离法
 */
public class HistogramRetrieval {

    /**
     * 传统的直方图相交法，统计RGB，归一化
     */
    public static double[][] GetHistogram1(BufferedImage img) {
        double[][] histgram = new double[3][256];
        int width = img.getWidth();             //图片宽度
        int height = img.getHeight();           //图片高度
        int pix[] = new int[width * height];    //像素个数
        int r, g, b;                            //记录R、G、B的值
        //将图片的像素值存到数组里
        pix = img.getRGB(0, 0, width, height, pix, 0, width);
        for (int i = 0; i < width * height; i++) {
            r = pix[i] >> 16 & 0xff;    //提取 Red
            g = pix[i] >> 8 & 0xff;     //提取 Green
            b = pix[i] & 0xff;          //提取 Blue
            histgram[0][r]++;
            histgram[1][g]++;
            histgram[2][b]++;
        }
        double red = 0, green = 0, blue = 0;
        for (int j = 0; j < 256; j++) {
            red += histgram[0][j];
            green += histgram[1][j];
            blue += histgram[2][j];
        }
        //将直方图每个像素值的总个数进行量化
        for (int j = 0; j < 256; j++) {
            histgram[0][j] /= red;
            histgram[1][j] /= green;
            histgram[2][j] /= blue;
        }
        return histgram;
    }

    /**
     * 传统的直方图相交法，统计RGB，归一化，后用相交来求两个图片的相似度
     */
    public static double GetSimilarity1(double[][] Rhistgram, double[][] Dhistgram) {
        double similar = 0.0;   //相似度
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < Rhistgram[i].length; j++) {
                similar += Math.min(Rhistgram[i][j], Dhistgram[i][j]);
            }
        }
        similar = similar / 3;
        return similar;
    }

    /**
     * 欧式距离求图片的相似度
     */
    public static double GetSimilarity2(double[][] Rhistgram, double[][] Dhistgram) {
        double similar = 0.0;  //相似度
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < Rhistgram[i].length; j++) {
                similar += (Rhistgram[i][j] - Dhistgram[i][j]) * (Rhistgram[i][j] - Dhistgram[i][j]);
            }
        }
        similar = similar / 6;
        similar = Math.sqrt(similar);
        return similar;
    }

    /**
     * 巴士系数法
     * 巴氏距离（巴塔恰里雅距离 / Bhattacharyya distance）,  需要除以总元素个个数
     * 注意：在颜色直方图的相似度比较中，巴氏距离效果最好
     * http://baike.baidu.com/item/%E5%B7%B4%E6%B0%8F%E8%B7%9D%E7%A6%BB
     */
    public static double[][] getBhattacharyyaDistance(BufferedImage img) {
        double[][] histgram = new double[3][256];
        int width = img.getWidth();             //图片宽度
        int height = img.getHeight();           //图片高度
        int pix[] = new int[width * height];    //像素个数
        int r, g, b;//记录R、G、B的值
        pix = img.getRGB(0, 0, width, height, pix, 0, width);//将图片的像素值存到数组里
        for (int i = 0; i < width * height; i++) {
            r = pix[i] >> 16 & 0xff; //提取R
            g = pix[i] >> 8 & 0xff;
            b = pix[i] & 0xff;
            histgram[0][r]++;
            histgram[1][g]++;
            histgram[2][b]++;
        }
        //将直方图每个像素值的总个数进行量化
        for (int j = 0; j < 256; j++) {
            for (int i = 0; i < 3; i++) {
                histgram[i][j] = histgram[i][j] / (width * height);
            }
        }
        return histgram;
    }

    public static double GetSimilarity(double[][] Rhistgram, double[][] Dhistgram) {
        double similar = 0.0;  //相似度
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < Rhistgram[i].length; j++) {
                similar += Math.sqrt(Rhistgram[i][j] * Dhistgram[i][j]);
            }
        }
        similar = similar / 3;
        return similar;
    }

    public static void main(String[] args) throws IOException {
//        InputStream aFile = HistogramRetrieval.class.getResourceAsStream("/images/25-边牧(小图).jpeg");
        InputStream aFile = HistogramRetrieval.class.getResourceAsStream("/images/after-01.jpg");
//        InputStream aFile = HistogramRetrieval.class.getResourceAsStream("/images/787.jpg");

//        InputStream bFile = HistogramRetrieval.class.getResourceAsStream("/images/25-边牧.jpeg");
        InputStream bFile = HistogramRetrieval.class.getResourceAsStream("/images/after-04.jpg");
//        InputStream bFile = HistogramRetrieval.class.getResourceAsStream("/images/787-旋转缩小.jpg");

        BufferedImage aPic = ImageIO.read(aFile);
        BufferedImage bPic = ImageIO.read(bFile);

        if (aPic == null || bPic == null) {
            throw new IllegalArgumentException("aPic or bPic == null!");
        }

        double similarity = 0.0;
        double[][] aHistogram = null;
        double[][] bHistogram = null;

        /**
         * 巴士系数方法
         */
        aHistogram = getBhattacharyyaDistance(aPic);
        bHistogram = getBhattacharyyaDistance(bPic);
        similarity = GetSimilarity(aHistogram, bHistogram);
        System.out.println("巴士系数方法：" + similarity);

        //转 JSON
//        System.out.println(JSON.toJSONString(aHistogram));
//        System.out.println(JSON.toJSONString(bHistogram));

        /**
         * 传统的RGB直方图相交法求得的图像相似度
         */
        aHistogram = GetHistogram1(aPic);
        bHistogram = GetHistogram1(bPic);
        similarity = GetSimilarity1(aHistogram, bHistogram);
        System.out.println("传统的RGB直方图相交法求得的图像相似度：" + similarity);

        /**
         * 传统的欧式距离求得的图像相似度
         */
        aHistogram = GetHistogram1(aPic);
        bHistogram = GetHistogram1(bPic);
        similarity = 1 - GetSimilarity2(aHistogram, bHistogram);
        System.out.println("传统的欧式距离求得的图像相似度：" + similarity);
    }

}
